ci: enforce exact (pinned) dependency versions to harden supply chain - #44
ci: enforce exact (pinned) dependency versions to harden supply chain#44goastler wants to merge 1 commit into
Conversation
✅ Deploy Preview for peaceful-pothos-9e62ce ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
01e20f8 to
7f9ad4e
Compare
Add a pinned-versions GitHub workflow that runs on PRs and fails if any dependencies/devDependencies/optionalDependencies entry in a package.json uses a floating range (^, ~, *, latest, ...), or if package-lock.json is missing integrity hashes for resolved registry packages. peerDependencies are exempt as they intentionally express compatibility ranges. Floating ranges let `npm install` silently pull a newer, unreviewed release - the vector behind recent npm supply-chain attacks: the Shai-Hulud worm (Sep 2025, 500+ packages), the chalk/debug maintainer-phish hijack (Sep 2025, ~2.6B weekly downloads), nx (Aug 2025), ua-parser-js (2021) and event-stream (2018). Pinning exact versions + committed lockfile + `npm ci` blocks the auto-pull of a malicious release until a version bump is explicitly reviewed. Existing floating specifiers are pinned to the exact version already resolved in package-lock.json (no change to what installs); the lockfile's recorded ranges are synced to match so `npm ci` stays in sync.
7f9ad4e to
00b93ee
Compare
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
What
Adds a
pinned-versionsGitHub Actions workflow (runs on PRs) that fails if any dependency is not pinned to an exact version, and pins all existing floating specifiers.The check (
.github/scripts/check-pinned-versions.mjs, pure Node, no install needed) flags:dependencies/devDependencies/optionalDependenciesentry using a floating range (^,~,*,latest,>=, …)package-lock.jsonregistry package missing anintegrityhashWhy — supply-chain hardening
Floating ranges let
npm installsilently pull a newer, unreviewed release. That is exactly the vector behind recent npm attacks:@ctrl/tinycolor, ~2.2M weekly downloads) to steal npm/cloud tokens and auto-publish.postinstallharvested SSH keys, npm tokens and wallets.Exact versions + committed lockfile +
npm cimean a malicious new release is not pulled until the version is explicitly bumped and reviewed.peerDependencies / optionalDependencies
fseventspinned to its resolved version).Safety
Existing floating specifiers were rewritten to the exact version already resolved in
package-lock.json, so this changes nothing about what installs. The lockfile's recorded ranges were synced to match (no resolved-version/integrity changes) sonpm cistays in sync.